package com.github.framework.beans.support;

import com.github.framework.beans.config.BeanDefinition;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @program: springv2
 * @description: BeanDefinition解析器
 * @author: Pop
 * @create: 2021-06-20 22:38
 **/
public class BeanDefinitionReader {

    private String[] configLocations;

    private Properties properties = new Properties();

    // 保存扫描的结果
    private List<String> registryBeanClassees = new ArrayList<String>();

    public Properties getConfig(){
        return properties;
    }

    public BeanDefinitionReader(String[] configLocations) {
        this.configLocations = configLocations;
        // 加载配置文件
        doLoadConfig(configLocations[0]);
        //扫描包
        doScanner(properties.getProperty("scanPackage"));
    }

    private void doLoadConfig(String contextConfigLocation) {
        InputStream inputStream=this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(null!=inputStream)
            {
                try{
                    inputStream.close();
                }catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 将扫描到的类初始化到一个享元模式的容器中去。
     * @param scanPackage
     */
    private void doScanner(String scanPackage) {
        // 由于配置的是包路径，这里只需要替换成文件路径就行
        URL url=this.getClass().getClassLoader().
                getResource("/"+scanPackage.replaceAll("\\.","/"));
        File file = new File(url.getFile());

        //当成是文件夹去扫描下的所有文件
        for(File f:file.listFiles())
        {
            if(f.isDirectory()){
                //需要递归再次查找
                doScanner(scanPackage+"."+f.getName());
            }
            else{
                //名字转换成类名，这个时候获得也许是文件夹，也可能是.class文件
                if(!f.getName().endsWith(".class")){
                    continue;
                }

                String className = scanPackage+"."+f.getName().replace(".class","");
                registryBeanClassees.add(className);
            }


        }
    }

    public List<BeanDefinition> loadBeanDefinitions() {

        List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();

        try {
            for(String className:registryBeanClassees)
            {
                //要注册到缓存里
                // 一个是类的全路径名
                // 一个beanName
                /*
                * 1.存在首字母小写
                * 2.自定义名字
                * 3.接口注入
                *
                * 这里需要说明的是，一个在ioc中的实例可能对应了多个key
                * 又或者说多个key指向的其实同一个实例
                * 不同的key其实对应的是不同的场景
                * 例如用于接口注入，他需要从ioc容器中取出实例，由于接口注入
                * 对应的是接口，你需要寻找该接口对应的实现来注入到指定的位置，那么
                * 这个时候实例后的value对应的key也许就是那个接口的beanName了。
                * 还有用户自定义的使用@Qualify指定的别名等，也同样指向哪一个实现
                * */
                Class<?> beanClass = Class.forName(className);
                if(beanClass.isInterface()){continue;}
                //保存类对应的ClassName（全类名）
                //还有beanName
                //1、默认是类名首字母小写
                beanDefinitions.add(doCreateBeanDefinition
                        (toLowerFirstCase(beanClass.getSimpleName()),
                                beanClass.getName()));
                //2、自定义
                //3、接口注入
                for (Class<?> i : beanClass.getInterfaces()) {
                    beanDefinitions.add(doCreateBeanDefinition(i.getName(),beanClass.getName()));
                }

            }

        }catch (Exception e)
        {
            e.printStackTrace();
        }

        return beanDefinitions;
    }

    /**
     * 创建BeanDefinition
     * @param beanName
     * @param beanClassName
     * @return
     */
    private BeanDefinition doCreateBeanDefinition(String beanName, String beanClassName) {
        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setFactoryBeanName(beanName);
        beanDefinition.setBeanClassName(beanClassName);
        return beanDefinition;
    }

    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0] +=32;
        return new String(chars);
    }

}
